/*
 * MIT License
 *
 * Copyright (c) 2019 Mr.css
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

package cn.seaboot.commons.handler;

import org.jetbrains.annotations.NotNull;

import java.util.Objects;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 队列元素；
 * 继承自{@link Delayed}，因为队列默认继承自{@link DelayQueue}，这两个是配合使用的。
 *
 * @author Mr.css on 2017年9月13日
 */
public class Message implements Delayed {
    /**
     * 消息超时时间
     */
    private long timeout;
    /**
     * 消息理论结束时间，实际执行时间会迟于这个时间
     */
    private long endTime;
    /**
     * 消息内容
     */
    private Object msg;
    /**
     * 负责处理这个消息的处理器对象
     */
    private Handler mHandler;
    /**
     * 一个静态的消息，如果消息能被及时处理，可以避免构造过多的消息对象
     */
    private static final Message MESSAGE = new Message();
    /**
     * 哑元元素，Looper 读取到哑元元素，跳出循环
     */
    private static final Message DUMMY = new Message();

    /**
     * 构造一个简单的Message，如果已经确定队列中的消息能被及时处理，
     * 可以不需要构造过多的Message对象，可以更有效率地使用内存。
     *
     * @return Message
     */
    public static Message obtain() {
        return Message.MESSAGE;
    }

    /**
     * 返回哑元元素
     *
     * @return Message
     */
    public static Message dummy() {
        return Message.MESSAGE;
    }

    /**
     * 判断当前对象是不是哑元元素
     *
     * @return true/false
     */
    public boolean isDummy() {
        return Message.DUMMY.equals(this);
    }

    public Message() {
    }

    Message(Object msg, Handler handler, long timeoutMillis) {
        this.msg = msg;
        this.mHandler = handler;
        this.timeout = timeoutMillis;
    }

    /**
     * setter
     *
     * @param mMessage 实际消息体
     */
    public void setMessage(Object mMessage) {
        this.msg = mMessage;
    }

    /**
     * setter
     *
     * @param timeoutMillis 超时时间
     */
    void setTimeout(long timeoutMillis) {
        this.timeout = timeoutMillis;
        this.endTime = System.currentTimeMillis() + timeoutMillis;
    }

    /**
     * setter
     *
     * @param mHandler -
     */
    void setHandler(Handler mHandler) {
        this.mHandler = mHandler;
    }

    /**
     * 返回消息的处理器
     *
     * @return Handler
     */
    Handler getHandler() {
        return this.mHandler;
    }

    /**
     * 返回消息主体
     *
     * @return Object
     */
    Object getMsg() {
        return this.msg;
    }

    /**
     * 获取消息延迟时间
     *
     * @return timeout
     */
    public long getTimeout() {
        return timeout;
    }

    /**
     * 重置超时时间，任务计划 3分钟后执行，一段时间后调用此函数，则时间继续顺延 3分钟
     * 如果任务已经超时，则不允许重置超时时间。
     */
    public void reset() throws TimeoutException {
        if (System.currentTimeMillis() > endTime) {
            throw new TimeoutException("The current object has timeout, can not be reset");
        }
        this.endTime = System.currentTimeMillis() + timeout;
    }

    /**
     * 计算任务延迟时间，如果时间小于0，则触发任务执行
     *
     * @param unit useless, always using MILLISECONDS
     */
    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(endTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }

    /**
     * 如果队列使用的是{@link DelayQueue}，消息最终会采用自然排序，通过此函数进行消息排序
     */
    @Override
    public int compareTo(@NotNull Delayed other) {
        if (other == this) {
            return 0;
        }
        long diff = this.endTime - ((Message) other).endTime;
        return (diff == 0) ? 0 : ((diff < 0) ? -1 : 1);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Message message = (Message) o;
        return timeout == message.timeout && endTime == message.endTime && Objects.equals(msg, message.msg) && Objects.equals(mHandler, message.mHandler);
    }

    @Override
    public int hashCode() {
        return Objects.hash(timeout, endTime, msg, mHandler);
    }
}